home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / AX25CMD.C < prev    next >
C/C++ Source or Header  |  1993-10-05  |  19KB  |  879 lines

  1. /*
  2. ** FILE: ax25cmd.c
  3. **
  4. ** AX.25 command handler.
  5. **
  6. ** 09/24/90 Bob Applegate, wa2zzx
  7. **    Added BCTEXT, BC, and BCINTERVAL commands for broadcasting an id
  8. **    string using UI frames.
  9. */
  10.  
  11. #include <stdio.h>
  12. #include <time.h>
  13. #include "global.h"
  14. #include "config.h"
  15. #ifdef AX25
  16. #include "mbuf.h"
  17. #include "timer.h"
  18. #include "proc.h"
  19. #include "iface.h"
  20. #include "ax25.h"
  21. #include "cmdparse.h"
  22. #include "socket.h"
  23. #include "session.h"
  24. #include "tty.h"
  25. #ifdef NETROM
  26. #include "nr4.h"
  27. #endif
  28. #include "commands.h"
  29. #include "asy.h"
  30. #ifdef SCC
  31. #include "scc.h"
  32. #endif
  33. #ifdef VANESSA
  34. #include "vanessa.h"
  35. #endif
  36.  
  37. #define next_seq(n)        (((n) + 1) & 7)
  38. #define SECSPERDAY    86400L
  39.  
  40. /* Defaults for IDing... */
  41. static char Axbctext[256];
  42.  
  43. int32 Axholdtime = AXROUTEHOLDTIME;
  44.  
  45. char *Ax25states[] = {
  46.     "",
  47.     "Disconnected",
  48.     "Listen",
  49.     "Conn pending",
  50.     "Disc pending",
  51.     "Connected",
  52.     "Time wait",
  53. };
  54.  
  55. char *Ax25reasons[] = {
  56.     "Normal",
  57.     "Reset",
  58.     "Timeout",
  59.     "Network",
  60. };
  61.  
  62. static void near
  63. axifout(int(*func)())
  64. {
  65.     struct iface *ifp;
  66.     int(*foo) __ARGS((struct iface *ifp));
  67.  
  68.     foo = func;
  69.  
  70.     for(ifp = Ifaces; ifp != NULLIF; ifp = ifp->next) {
  71.         if(ifp->output == ax_output) {
  72.             (*foo)(ifp);
  73.         }
  74.     }
  75. }
  76.  
  77. /* Verify that axp points to a valid ax25 control block */
  78. static int near
  79. ax25val(struct ax25_cb *axp,int(*func)(),char *s)
  80. {
  81.     struct ax25_cb *axp1;
  82.     int all = stricmp(s,"all"), (*foo) __ARGS((struct ax25_cb *axp1));
  83.  
  84.     foo = func;
  85.  
  86.     if(axp != NULLAX25) {
  87.         for(axp1 = Ax25_cb; axp1 != NULLAX25; axp1 = axp1->next) {
  88.             if(axp1->state != LISTEN && (!all || (all && (axp1 == axp)))) {
  89. /*                
  90.                 if(foo == st_ax25) {
  91.                     tprintf("&AXCB %lx  &Peer %lx\n",ptol(axp1),ptol(axp1->peer));
  92.                 }
  93. */                
  94.                 if(axp1->dama && foo == kick_ax25) {
  95.                     set_timer(&axp1->t1,T1init * 2000L);
  96.                 }
  97.                 (*foo)(axp1);
  98.  
  99.                 if(all) {
  100.                     return 0;
  101.                 }
  102.             }
  103.         }
  104.     }
  105.     if(all) {
  106.         tputs(Notval);
  107.         return -1;
  108.     }
  109.     return 0;
  110. }
  111.  
  112. /* This is the low-level broadcast function. */
  113. static int
  114. ax_bc(struct iface *ifp)
  115. {
  116.     /* prepare the header */
  117.     int i = strlen(Axbctext);
  118.     struct mbuf *hbp = alloc_mbuf(i);
  119.  
  120.     hbp->cnt = i;
  121.     memcpy(hbp->data,Axbctext,i);
  122.  
  123.     return (*ifp->output)(ifp,Ax25multi[2],ifp->hwaddr,PID_NO_L3,hbp);    /* send it */
  124. }
  125.  
  126. /* This function is called to send the current broadcast message */
  127. static int
  128. dobc(int argc,char **argv,void *p)
  129. {
  130.     if(argc < 2) {
  131.         axifout(ax_bc);
  132.     } else {
  133.         struct iface *ifp;
  134.  
  135.         while(--argc > 0) {
  136.             if((ifp = cmp_if(*++argv)) != NULLIF)
  137.                 ax_bc(ifp);
  138.         }
  139.     }
  140.     return 0;
  141. }
  142.  
  143. /* View/Change the message we broadcast. */
  144. static int
  145. dobctext(int argc,char **argv,void *p)
  146. {
  147.     if (argc < 2) {
  148.         if(Axbctext[0] != '\0')
  149.             tprintf("%s\n",Axbctext);
  150.     } else {
  151.         Axbctext[0] = '\0';
  152.  
  153.         while(--argc > 0) {
  154.             strcat(Axbctext,*++argv);
  155.             if(strlen(Axbctext) > 220)
  156.                 break;
  157.             strcat(Axbctext," ");
  158.         }
  159.         if(strlen(Axbctext) > 2) {
  160.             strcat(Axbctext,AX_EOL);
  161.             strcat(Axbctext,"\0");
  162.         } else
  163.             Axbctext[0] = '\0';
  164.     }
  165.     return 0;
  166. }
  167.  
  168. static int
  169. dobud(int argc,char **argv,void *p)
  170. {
  171.     char tmp[AXBUF];
  172.  
  173.     if(!(setcall(tmp,argv[1]))) {
  174.         is_bud(tmp,1);
  175.         return 0;
  176.     }
  177.     return -1;
  178. }
  179.  
  180. /* Force a retransmission */
  181. int
  182. kick_ax25(struct ax25_cb *axp)
  183. {
  184.     return ax25val(axp,t1_timeout,0);
  185. }
  186.  
  187. static int
  188. doaxclose(int argc,char **argv,void *p)
  189. {
  190.     return ax25val((struct ax25_cb *)shtop(argv[1]),disc_ax25,argv[1]);
  191. }
  192.  
  193. static int
  194. doaxreset(int argc,char **argv,void *p)
  195. {
  196.     return ax25val((struct ax25_cb *)shtop(argv[1]),reset_ax25,argv[1]);
  197. }
  198.  
  199. static int
  200. axheard(struct iface *ifp)
  201. {
  202.     int j;
  203.     struct lq *lp;
  204.     char *cp, tmp[AXBUF];
  205.  
  206.     if(ifp->hwaddr == NULLCHAR) {
  207.         return 0;
  208.     }
  209.     for(lp = ifp->lq, j = 0; j < ifp->Hcurrent; lp++, j++) {
  210.         cp = ctime(&lp->time);
  211.  
  212.         if(j == 0) {
  213.             tprintf("Iface: %s\n%-12s%-28s%-11s%s\n",
  214.                 ifp->name,"Call","heard","Call","heard");
  215.         }
  216.         if(!(j % 2))
  217.             cp[24] = '\0';
  218.         tprintf("%-10s%26s",pax25(tmp,lp->addr),cp);
  219.         if(!(j % 2))
  220.             tputs("    ");
  221.     }
  222.     if(j && (j % 2))
  223.         tputs("\n");
  224.     return 0;
  225. }
  226.  
  227. int
  228. doaxheard(int argc,char **argv,void *p)
  229. {
  230.     tprintf("System time: %s",ctime(&currtime));
  231.  
  232.     if(argc < 2) {
  233.         axifout(axheard);
  234.     } else {
  235.         struct iface *ifp;
  236.  
  237.         while(--argc > 0) {
  238.             if((ifp = cmp_if(*++argv)) != NULLIF)
  239.                 axheard(ifp);
  240.         }
  241.     }
  242.     return 0;
  243. }
  244.  
  245. static int
  246. doaxflush(int argc,char **argv,void *p)
  247. {
  248.     axifout(axflush);
  249.     return 0;
  250. }
  251.  
  252. static char * near
  253. pathtostr(struct ax25_cb *cp)
  254. {
  255.  
  256.   char  *ap, *p;
  257.   static char buf[128];
  258.  
  259.   if (!cp->pathlen) return "*";
  260.   p = buf;
  261.   ap = cp->path + AXALEN;
  262.   if (!addreq(ap,cp->iface->hwaddr)) {
  263.     pax25(p, ap);
  264.     while (*p) p++;
  265.     *p++ = '-';
  266.     *p++ = '>';
  267.   }
  268.   pax25(p, cp->path);
  269.   while (*p) p++;
  270.   while (!(ap[ALEN] & E)) {
  271.     ap += AXALEN;
  272.     *p++ = ',';
  273.     pax25(p, ap);
  274.     while (*p) p++;
  275.     if (ap[ALEN] & REPEATED) *p++ = '*';
  276.   }
  277.   *p = '\0';
  278.   return buf;
  279. }
  280.  
  281. /* Dump one control block */
  282. int
  283. st_ax25(struct ax25_cb *cp)
  284. {
  285.     int i;
  286.  
  287.     tprintf("Path: %s%s\nIface: %s  State: %s\n",
  288.         pathtostr(cp),
  289.         cp->dama ? " [DAMA]" : "",
  290.         cp->iface ? cp->iface->name : "???",
  291.         Ax25states[cp->state]);
  292.  
  293.     tprintf("Closed: %-5sPolling: %-5sREJsent: %-5sRNRsent: %-6sRNRrecv: ",
  294.         cp->closed    ? "Yes" : "No",
  295.         cp->polling ? "Yes" : "No",
  296.         cp->rejsent ? "Yes" : "No",
  297.         cp->rnrsent ? "Yes" : "No");
  298.  
  299.     if (cp->remotebusy) {
  300.       tprintf("%ld sec", currtime - cp->remotebusy);
  301.     } else {
  302.       tputs("No");
  303.     }
  304.     tprintf("\nCWind: %-6dRetry: %-7dUnack: %-7dSRT: %2ld sec    MDev: %2ld sec\nT1: ",
  305.         cp->cwind,
  306.         cp->retries,
  307.         cp->unack,
  308.         cp->srt / 1000L,
  309.         cp->mdev / 1000L);
  310.  
  311.     if (run_timer(&cp->t1)) {
  312.       tprintf("%ld",read_timer(&cp->t1) / 1000L);
  313.     } else {
  314.       tputs("-");
  315.     }
  316.     tprintf("/%ld sec  T2: ",dur_timer(&cp->t1) / 1000L);
  317.  
  318.     if (run_timer(&cp->t2)) {
  319.       tprintf("%ld",read_timer(&cp->t2) / 1000L);
  320.     } else {
  321.       tputs("-");
  322.     }
  323.     tprintf("/%ld sec  T3: ",dur_timer(&cp->t2) / 1000L);
  324.  
  325.     if (run_timer(&cp->t3)) {
  326.       tprintf("%ld",read_timer(&cp->t3) / 1000L);
  327.     } else {
  328.       tputs("-");
  329.     }
  330.     tprintf("/%ld sec  T4: ",dur_timer(&cp->t3) / 1000L);
  331.  
  332.     if (run_timer(&cp->t4)) {
  333.       tprintf("%ld",read_timer(&cp->t4) / 1000L);
  334.     } else {
  335.       tputs("-");
  336.     }
  337.     tprintf("/%ld sec  T5: ",dur_timer(&cp->t4) / 1000L);
  338.  
  339.     if (run_timer(&cp->t5)) {
  340.       tprintf("%ld",read_timer(&cp->t5) / 1000L);
  341.     } else {
  342.       tputs("-");
  343.     }
  344.     tprintf("/%ld sec\n",dur_timer(&cp->t5) / 1000L);
  345.  
  346.     if(cp->rxq) {
  347.         tprintf("Rcv queue: %d\n", len_p(cp->rxq));
  348.     }
  349.     if (cp->reseq[0].bp || cp->reseq[1].bp ||
  350.         cp->reseq[2].bp || cp->reseq[3].bp ||
  351.         cp->reseq[4].bp || cp->reseq[5].bp ||
  352.         cp->reseq[6].bp || cp->reseq[7].bp) {
  353.       tputs("Reassembly queue:\n");
  354.       for (i = next_seq(cp->vr); i != cp->vr; i = next_seq(i)) {
  355.         if (cp->reseq[i].bp) {
  356.           tprintf("           Seq %3d: %3d bytes\n",
  357.             i,len_p(cp->reseq[i].bp));
  358.         }
  359.       }
  360.     }
  361.     if(cp->txq) {
  362.         tprintf("Snd queue: %d\n", len_p(cp->txq));
  363.     }
  364.     if (cp->rxasm) {
  365.       struct mbuf *bp;
  366.  
  367.       tputs("Resend queue:\n");
  368.       for (i = 0, bp = cp->rxasm; bp; i++, bp = bp->anext) {
  369.         tprintf("           Seq %3d: %3d bytes\n",
  370.           (cp->vs - cp->unack + i) & 7,len_p(bp));
  371.       }
  372.     }
  373.   return 0;
  374. }
  375.  
  376. /* Display AX.25 link level control blocks */
  377. static int
  378. doaxstat(int argc,char **argv,void *p)
  379. {
  380.     struct ax25_cb *axp;
  381.     char tmp[AXBUF];
  382.  
  383.     if(argc < 2){
  384.         tputs("&AXCB Rcv-Q Snd-Q  Local     Remote    Iface     State\n");
  385.         for(axp = Ax25_cb; axp != NULLAX25; axp = axp->next){
  386.             if(axp->state == LISTEN) {
  387.                 tprintf("%lx%55s\n",ptol(axp),"Listen (S)");
  388.                 continue;
  389.             }
  390.             tprintf("%lx %6d%6d  %-10s",
  391.                 ptol(axp),len_p(axp->rxq),len_p(axp->txq),pax25(tmp,axp->path + AXALEN));
  392.             tprintf("%-10s%-10s%s\n",
  393.                 pax25(tmp,axp->path),
  394.                 axp->iface ? axp->iface->name : "???",
  395.                 Ax25states[axp->state]);
  396.         }
  397.         return 0;
  398.     }
  399.     return ax25val((struct ax25_cb *)shtop(argv[1]),st_ax25,argv[1]);
  400. }
  401.  
  402. /* Display or change our AX.25 address */
  403. static int
  404. domycall(int argc,char **argv,void *p)
  405. {
  406.     char tmp[AXBUF];
  407.  
  408.     if(argc < 2){
  409.         tprintf("%s\n",pax25(tmp,Mycall));
  410.     } else {
  411.         if(setcall(Mycall,argv[1]) == -1)
  412.             return -1;
  413.     }
  414.     return 0;
  415. }
  416.  
  417. /* Control AX.25 digipeating */
  418. static int
  419. dodigipeat(int argc,char **argv,void *p)
  420. {
  421.     struct iface *ifp;
  422.  
  423.     if((ifp = cmp_if(argv[1])) != NULLIF)
  424.         return setintrc(&ifp->flags->digipeat,"Digipeat",--argc,++argv,0,2);
  425.     return -1;
  426. }
  427.  
  428. static int
  429. dot1(int argc,char **argv,void *p)
  430. {
  431.     struct iface *ifp;
  432.  
  433.     if((ifp = cmp_if(argv[1])) != NULLIF)
  434.         return setintrc(&ifp->flags->t1init,"T1init",--argc,++argv,3,30);
  435.     return -1;
  436. }
  437.  
  438. static int
  439. dot2(int argc,char **argv,void *p)
  440. {
  441.     struct iface *ifp;
  442.  
  443.     if((ifp = cmp_if(argv[1])) != NULLIF)
  444.         return setintrc(&ifp->flags->t2init,"T2init",--argc,++argv,0,ifp->flags->t1init/2);
  445.     return -1;
  446. }
  447.  
  448. static int
  449. dot3(int argc,char **argv,void *p)
  450. {
  451.     struct iface *ifp;
  452.  
  453.     if((ifp = cmp_if(argv[1])) != NULLIF)
  454.         return setintrc(&ifp->flags->t3init,"T3init",--argc,++argv,0,3600);
  455.     return -1;
  456. }
  457.  
  458. static int
  459. dot4(int argc,char **argv,void *p)
  460. {
  461.     struct iface *ifp;
  462.  
  463.     if((ifp = cmp_if(argv[1])) != NULLIF)
  464.         return setintrc(&ifp->flags->t4init,"T4init",--argc,++argv,ifp->flags->t1init*2,ifp->flags->t1init*20);
  465.     return -1;
  466. }
  467.  
  468. static int
  469. dot5(int argc,char **argv,void *p)
  470. {
  471.     struct iface *ifp;
  472.  
  473.     if((ifp = cmp_if(argv[1])) != NULLIF)
  474.         return setintrc(&ifp->flags->t5init,"T5init",--argc,++argv,0,ifp->flags->t2init-1);
  475.     return -1;
  476. }
  477.  
  478. /* Set retry limit count */
  479. static int
  480. doretries(int argc,char **argv,void *p)
  481. {
  482.     struct iface *ifp;
  483.  
  484.     if((ifp = cmp_if(argv[1])) != NULLIF)
  485.         return setintrc(&ifp->flags->retries,"Retries",--argc,++argv,0,50);
  486.     return -1;
  487. }
  488.  
  489. /* Force a retransmission */
  490. static int
  491. doaxkick(int argc,char **argv,void *p)
  492. {
  493.     return ax25val((struct ax25_cb *)shtop(argv[1]),kick_ax25,argv[1]);
  494. }
  495.  
  496. /* Set maximum number of frames that will be allowed in flight */
  497. static int
  498. domaxframe(int argc,char **argv,void *p)
  499. {
  500.     struct iface *ifp;
  501.  
  502.     if((ifp = cmp_if(argv[1])) != NULLIF)
  503.         return setintrc(&ifp->flags->maxframe,"Maxframe",--argc,++argv,1,7);
  504.     return -1;
  505. }
  506.  
  507. static int
  508. domaxheard(int argc,char **argv,void *p)
  509. {
  510.     struct iface *ifp;
  511.  
  512.     if((ifp = cmp_if(argv[1])) != NULLIF) {
  513.         if(argc < 3) {
  514.             int16 i = ifp->Hmax;
  515.             setintrc(&i,"Maxheard",--argc,++argv,2,40);
  516.             ifp->Hmax = i;
  517.         } else {
  518.             return(maxheard(ifp,atoi(argv[2])));
  519.         }
  520.     }
  521.     return -1;
  522. }
  523.  
  524. static int
  525. dot3disc(int argc,char **argv,void *p)
  526. {
  527.     struct iface *ifp;
  528.  
  529.     if((ifp = cmp_if(argv[1])) != NULLIF)
  530.         return setintrc(&ifp->flags->t3disc,"T3disc",--argc,++argv,0,1);
  531.     return -1;
  532. }
  533.  
  534. /* Set maximum length of I-frame data field */
  535. static int
  536. dopaclen(int argc,char **argv,void *p)
  537. {
  538.     struct iface *ifp;
  539.  
  540.     if((ifp = cmp_if(argv[1])) != NULLIF)
  541.         return setintrc(&ifp->flags->paclen,"Paclen",--argc,++argv,1,2048);
  542.     return -1;
  543. }
  544.  
  545. /* Set size of I-frame above which polls will be sent after a timeout */
  546. static int
  547. dopthresh(int argc,char **argv,void *p)
  548. {
  549.     struct iface *ifp;
  550.  
  551.     if((ifp = cmp_if(argv[1])) != NULLIF)
  552.         return setintrc(&ifp->flags->pthresh,"Pthresh",--argc,++argv,0,ifp->flags->paclen);
  553.     return -1;
  554. }
  555.  
  556. /* Set high water mark on receive queue that triggers RNR */
  557. static int
  558. doaxwindow(int argc,char **argv,void *p)
  559. {
  560.     struct iface *ifp;
  561.  
  562.     if((ifp = cmp_if(argv[1])) != NULLIF)
  563.         return setintrc(&ifp->flags->axwindow,"Axwindow",
  564.             --argc,++argv,ifp->flags->paclen/2,ifp->flags->paclen*8);
  565.     return -1;
  566. }
  567. /* End of ax25 subcommands */
  568.  
  569. /* Initiate interactive AX.25 connect to remote station */
  570. int
  571. doconnect(int argc,char **argv,void *p)
  572. {
  573.   char  *ap, tmp[AXBUF], path[10*AXALEN];
  574.   struct session *sp;
  575.   struct iface *ifp;
  576.   struct sockaddr_ax fsocket;
  577.   struct ax25_cb axp;
  578.  
  579.   argc--;
  580.   argv++;
  581.  
  582.   if((ifp = if_lookup(*argv)) != NULLIF) {
  583.     if(ifp->output != ax_output) {
  584.       tprintf(Badax,*argv);
  585.       return -1;
  586.     }
  587.     argc--;
  588.     argv++;
  589.   }
  590.   for (ap = path; argc > 0; argc--, argv++) {
  591.     if (!strncmp("via", *argv, strlen(*argv))) continue;
  592.     if (ap >= path + sizeof(path)) {
  593.       tputs("Max 8 digis\n");
  594.       return -1;
  595.     }
  596.     if (setcall(ap, *argv)) {
  597.       tprintf(Invcall, *argv);
  598.       return -1;
  599.     }
  600.     ap[ALEN] &= ~E;
  601.  
  602.     if (ap == path) {
  603.       ap += AXALEN;
  604.       addrcp(ap,Mycall);
  605.       ap[ALEN] &= ~E;
  606.     }
  607.     ap += AXALEN;
  608.   }
  609.   if (ap < path + 2 * AXALEN) {
  610.     tputs("Missing call\n");
  611.     return -1;
  612.   }
  613.   ap[-1] |= E;
  614.  
  615.   memset(&axp,0,sizeof(struct ax25_cb));
  616.  
  617.   build_path(&axp,ifp,path,0);
  618.  
  619.   if(!axp.iface) {
  620.     tputs("No specified iface\n");
  621.     return -1;
  622.   }
  623.  
  624.   addrcp(axp.path + AXALEN,axp.iface->hwaddr);
  625.  
  626.   pax25(tmp,axp.path);
  627.  
  628.   if(callreq(axp.path)) {
  629.     tprintf(Invcall,tmp);
  630.     return -1;
  631.   }
  632.   axroute_add(&axp,0);
  633.  
  634.   /* Allocate a session descriptor */
  635.   if((sp = newsession(tmp,AX25TNC,SPLIT | SWAP)) == NULLSESSION){
  636.     tputs(Nosess);
  637.     return -1;
  638.   }
  639.  
  640.   if((sp->s = socket(AF_AX25,SOCK_STREAM,0)) == -1){
  641.     tputs(Nosocket);
  642.     goto quit;
  643.   }
  644.  
  645.   fsocket.sax_family = AF_AX25;
  646.   memcpy(fsocket.ax25_addr,axp.path,AXALEN);
  647.   memcpy(fsocket.iface,axp.iface->name,ILEN);
  648.   if(!(tel_connect(sp,(char *)&fsocket,SOCKSIZE)))
  649.     return 0;
  650. quit:
  651.   keywait(NULLCHAR,1);
  652.   freesession(sp);
  653.   return -1;
  654. }
  655.  
  656. static int
  657. dorouteadd(int argc,char **argv,void *p)
  658. {
  659.  
  660.   char  *ap;
  661.   int  perm;
  662.   struct ax25_cb cb;
  663.  
  664.   argc--;
  665.   argv++;
  666.  
  667.   if((perm = !strcmp(*argv, "permanent")) != 0) {
  668.     argc--;
  669.     argv++;
  670.   }
  671.   if((cb.iface = cmp_if(*argv)) == NULLIF)
  672.     return -1;
  673.  
  674.   argc--;
  675.   argv++;
  676.  
  677.   if (!strcmp(*argv,"default")) {
  678.     axroute_default_ifp = cb.iface;
  679.     return 0;
  680.   }
  681.   for (ap = cb.path; argc > 0; argc--, argv++) {
  682.     if (!strncmp("via", *argv, strlen(*argv)))
  683.       continue;
  684.     if (ap >= cb.path + sizeof(cb.path)) {
  685.       tputs("Max 8 digis\n");
  686.       return -1;
  687.     }
  688.     if (setcall(ap, *argv)) {
  689.       tprintf(Invcall, *argv);
  690.       return -1;
  691.     }
  692.     if (ap == cb.path) {
  693.       ap += AXALEN;
  694.       addrcp(ap,cb.iface->hwaddr);
  695.     }
  696.     ap += AXALEN;
  697.   }
  698.   if (ap < cb.path + 2 * AXALEN) {
  699.     tputs("Missing call\n");
  700.     return -1;
  701.   }
  702.   ap[-1] |= E;
  703.   cb.pathlen = (int)(ap - cb.path);
  704.   axroute_add(&cb, perm);
  705.   return 0;
  706. }
  707.  
  708. #include "smtp.h"        /* for 'Months' */
  709.  
  710. static void near
  711. doroutelistentry(struct axroute_tab *rp)
  712. {
  713.   struct axroute_tab *rp_stack[20];
  714.   struct iface *ifp;
  715.   struct tm *tm = gmtime(&rp->time);
  716.   int i, n, perm = rp->perm;
  717.   char *buf = mxallocw(10*AXBUF), *cp = buf;
  718.  
  719.   pax25(cp,(char *)&rp->call);
  720.  
  721.   for (n = 0; rp; rp = rp->digi) {
  722.     rp_stack[++n] = rp;
  723.     ifp = rp->ifp;
  724.   }
  725.   for (i = n; i > 1; i--) {
  726.     strcat(cp, i == n ? " via " : ",");
  727.     while (*cp)    cp++;
  728.     pax25(cp,(char *)&rp_stack[i]->call);
  729.   }
  730.  
  731.   tprintf("%2d-%.3s  %02d:%02d  %-9s  %c %s\n",
  732.     tm->tm_mday,
  733.     Months[tm->tm_mon],
  734.     tm->tm_hour,
  735.     tm->tm_min,
  736.     ifp ? ifp->name : "???",
  737.     perm ? '*' : ' ',
  738.     buf);
  739.  
  740.   xfree(buf);
  741. }
  742.  
  743. int
  744. doroutelist(int argc,char **argv,void *p)
  745. {
  746.   struct axroute_tab *rp;
  747.  
  748.   tputs("Date    GMT    Interface  P Path\n");
  749.  
  750.   if (argc < 2) {
  751.     for (rp = Axroute_tab; rp; rp = rp->next) {
  752.       doroutelistentry(rp);
  753.     }
  754.   } else {
  755.     int i;
  756.     struct ax25_addr call;
  757.     char tmp[AXBUF];
  758.  
  759.     for(i = 1; i < argc; i++) {
  760.       if(setcall((char *)&call,argv[i]) || (rp = axroute_tabptr(&call,0)) == 0) {
  761.         tprintf("*** no route to %s\n",pax25(tmp,(char *)&call));
  762.       } else {
  763.         doroutelistentry(rp);
  764.       }
  765.     }
  766.   }
  767.   return 0;
  768. }
  769.  
  770. static int
  771. doroutehold(int argc,char **argv,void *p)
  772. {
  773.     int16 holdtime = (int16)(Axholdtime / SECSPERDAY);
  774.  
  775.     if(setintrc(&holdtime,"Holdtime (days)",argc,argv,1,120) == 0) {
  776.         Axholdtime = (int32)(holdtime * SECSPERDAY);
  777.         return 0;
  778.     }
  779.     return -1;
  780. }
  781.  
  782. static int
  783. doroutestat(int argc,char **argv,void *p)
  784. {
  785.   struct ifptable_t {
  786.     struct iface *ifp;
  787.     int count;
  788.   } ;
  789.  
  790.   int dev, total;
  791.   struct axroute_tab *rp, *dp;
  792.   struct iface *ifp;
  793.   struct ifptable_t ifptable[ASY_MAX
  794. #ifdef SCC
  795.                                      + MAXSCC
  796. #endif
  797. #ifdef VANESSA
  798.                                               + VAN_MAX
  799. #endif
  800. #ifdef AXIP
  801.                                                         + NAX25
  802. #endif
  803.                                                                ];
  804.   memset(ifptable,0,sizeof(ifptable));
  805.  
  806.   for (ifp = Ifaces; ifp; ifp = ifp->next)
  807.     if (ifp->output == ax_output)
  808.       ifptable[ifp->niface].ifp = ifp;
  809.  
  810.   for (rp = Axroute_tab; rp; rp = rp->next) {
  811.     for (dp = rp; dp->digi; dp = dp->digi) ;
  812.     if (dp->ifp)
  813.       ifptable[dp->ifp->niface].count++;
  814.   }
  815.   tputs("Interface  Count\n");
  816.   for (total = 0, dev = 0; dev < Niface; dev++) {
  817.     if(ifptable[dev].count /*|| ifptable[dev].ifp == axroute_default_ifp*/) {
  818.       tprintf("%c %-7s  %5d\n",
  819.         ifptable[dev].ifp == axroute_default_ifp ? '*' : ' ',
  820.         ifptable[dev].ifp->name,
  821.         ifptable[dev].count);
  822.       total += ifptable[dev].count;
  823.     }
  824.   }
  825.   tprintf("  total    %5d\n", total);
  826.   return 0;
  827. }
  828.  
  829. /* Display and modify AX.25 routing table */
  830. static int
  831. doaxroute(int argc,char **argv,void *p)
  832. {
  833.   struct cmds Routecmds[] = {
  834.     {"add",  dorouteadd,  0, 3, "ax25 route add [permanent] <iface> <path>"},
  835.     {"hold", doroutehold, 0, 0, NULLCHAR},
  836.     {"list", doroutelist, 0, 0, NULLCHAR},
  837.     {"stat", doroutestat, 0, 0, NULLCHAR},
  838.     {NULLCHAR, NULLFP,    0, 0, NULLCHAR}
  839.   };
  840.   return subcmd(Routecmds,argc,argv,p);
  841. }
  842.  
  843. /* Multiplexer for top-level ax25 command */
  844. int
  845. doax25(int argc,char **argv,void *p)
  846. {
  847.     struct cmds Axcmds[] = {
  848.         {"bc",            dobc,        0, 0, NULLCHAR},
  849.         {"bctext",        dobctext,    0, 0, NULLCHAR},
  850.         {"bud",            dobud,        0, 2, "ax25 bud <call>"},
  851.         {"close",        doaxclose,    0, 2, "ax25 close <axcb>"},
  852.         {"digipeat",    dodigipeat,    0, 2, "ax25 digipeat <iface>"},
  853.         {"flush",        doaxflush,    0, 0, NULLCHAR},
  854.         {"heard",       doaxheard,    0, 0, NULLCHAR},
  855.         {"kick",        doaxkick,    0, 2, "ax25 kick <axcb>"},
  856.         {"maxframe",    domaxframe,    0, 2, "ax25 maxframe <iface>"},
  857.         {"maxheard",    domaxheard, 0, 2, "ax25 maxheard <iface>"},
  858.         {"mycall",        domycall,    0, 0, NULLCHAR},
  859.         {"paclen",        dopaclen,    0, 2, "ax25 paclen <iface>"},
  860.         {"pthresh",        dopthresh,    0, 0, NULLCHAR},
  861.         {"reset",        doaxreset,    0, 2, "ax25 reset <axcb>"},
  862.         {"retry",        doretries,    0, 2, "ax25 retry <iface>"},
  863.         {"route",        doaxroute,    0, 0, NULLCHAR},
  864.         {"status",        doaxstat,    0, 0, NULLCHAR},
  865.         {"t1",            dot1,        0, 2, "ax25 t1 <iface>"},
  866.         {"t2",            dot2,        0, 2, "ax25 t2 <iface>"},
  867.         {"t3",            dot3,        0, 2, "ax25 t3 <iface>"},
  868.         {"t3disc",        dot3disc,    0, 2, "ax25 t3disc <iface>"},
  869.         {"t4",            dot4,        0, 2, "ax25 t4 <iface>"},
  870.         {"t5",            dot5,        0, 2, "ax25 t5 <iface>"},
  871.         {"window",        doaxwindow,    0, 2, "ax25 window <iface>"},
  872.         {NULLCHAR,      NULLFP,     0, 0, NULLCHAR}
  873.     };
  874.     return subcmd(Axcmds,argc,argv,p);
  875. }
  876.  
  877.  
  878. #endif
  879.